
import unittest
import operator

class MockConsole(object):
    output_buffer = []
    
    @staticmethod
    def write(astring):
        MockConsole.output_buffer.append(astring)
        
    @staticmethod
    def clear():
        del MockConsole.output_buffer[:]
        
def mylog(when):
    # --------------- nest function
    def _print_to_console(func,argtuple):
        # '%s' means call str() on input arguments
        MockConsole.write("%s%s"%(func.__name__,argtuple))
        
    def _pre_log(func):
        """
        decorator is a function factory, which take a function, and return another function, which wraps the input one
        so what it returns must be a function, which has only one argument, which is a function
        """
        def wrapper(*args):
            _print_to_console(func,args)
            return func(*args)
        return wrapper
    
    def _post_log(func):
        def wrapper(*args):
            returnvalue = func(*args)
            _print_to_console(func,args)
            return returnvalue
        return wrapper
    # --------------- end nest function
    
    # *************** decorator body
    return {"before":_pre_log,"after":_post_log}[when]
    # *************** end decorator body
    
@mylog("before")
def logged_add(*args):
    result = reduce(operator.add,args)
    MockConsole.write("result=%d"%result)
    return result

@mylog("after")
def multiple_logged(*args):
    result = reduce(operator.mul,args)
    MockConsole.write("result=%d"%result)
    return result

class TestDecorator(unittest.TestCase):
    def tearDown(self):
        MockConsole.clear()
        
    def test_mybefore_log(self):
        self.assertEqual(15,logged_add(4,5,6))
        self.assertEqual(["logged_add(4, 5, 6)","result=15"],MockConsole.output_buffer)
        
    def test_myafter_log(self):
        self.assertEqual(60,multiple_logged(3,4,5))
        self.assertEqual(["result=60","multiple_logged(3, 4, 5)"],MockConsole.output_buffer)
        
if __name__ == "__main__":
    unittest.main()
        
